/*

 */
package com.gper.edu.future

import scala.util._

case class Water(temperature: Int)

/**
 * 假设你想准备一杯卡布奇诺，你可以一个接一个的执行以下步骤：
 *
 * 1. 研磨所需的咖啡豆
 * 2. 加热一些水
 * 3. 用研磨好的咖啡豆和热水制做一杯咖啡
 * 4. 打奶泡
 * 5. 结合咖啡和奶泡做成卡布奇诺
 */
object CappuccinoDemo {
  type CoffeeBeans = String
  type GroundCoffee = String
  type Milk = String
  type FrothedMilk = String
  // S 打奶泡
  type Espresso = String
  type Cappuccino = String

  // dummy implementations of the individual steps:
  def grind(beans: CoffeeBeans): GroundCoffee = {
    println(s"1. 研磨所需的咖啡豆 :$beans")
    s"ground coffee of $beans"
  }

  def heatWater(water: Water): Water = {
    println(s"2. 加热一些水 :${water.temperature}")
    water.copy(temperature = 85)
  }

  def frothMilk(milk: Milk): FrothedMilk = {
    println("4. 打奶泡")
    s"frothed $milk"
  }

  def brew(coffee: GroundCoffee, heatedWater: Water): Espresso = {
    println("3. 用研磨好的咖啡豆和热水制做一杯咖啡")
    "espresso"
  }

  def combine(espresso: Espresso, frothedMilk: FrothedMilk): Cappuccino = {
    println("5. 结合咖啡和奶泡做成卡布奇诺")
    "cappuccino"
  }

  // some exceptions for things that might go wrong in the individual steps
  // (we'll need some of them later, use the others when experimenting with the code):
  case class GrindingException(msg: String) extends Exception(msg)

  case class FrothingException(msg: String) extends Exception(msg)

  case class WaterBoilingException(msg: String) extends Exception(msg)

  case class BrewingException(msg: String) extends Exception(msg)

  def prepareCappuccino(): Try[Cappuccino] = for {
    ground <- Try(grind("arabica beans"))
    water <- Try(heatWater(Water(25)))
    espresso <- Try(brew(ground, water))
    foam <- Try(frothMilk("milk"))
  } yield combine(espresso, foam)

  def main(args: Array[String]): Unit = {
    val result = prepareCappuccino();
    result match {
      case Success(value) => println(s"your Cappuccino: $value")
      case Failure(ex) => println(s"your Cappuccino is made failed: ${ex.getMessage}")
    }
  }
}
